/*
 * Copyright (C) 2017 OTA keys S.A.
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License v2.1. See the file LICENSE in the top level
 * directory for more details.
 */

/**
 * @{
 *
 * @file
 */
#include "hwboard.h"
// #include "vflash_fs.h"
// #include "mtd.h"
#include "board.h"

#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

#include "embUnit.h"

/* Define MTD_0 in board.h to use the board mtd if any */
/* Test mock object implementing a simple RAM-based mtd */
#ifndef SECTOR_COUNT
#define SECTOR_COUNT 16
#endif
#ifndef PAGE_PER_SECTOR
#define PAGE_PER_SECTOR 4
#endif
#ifndef PAGE_SIZE
#define PAGE_SIZE 64
#endif

static void test_littlefs_setup(void)
{
    // littlefs_desc.dev = _dev;
    flash_fs.mount();
    int res = flash_fs.mkdir("/test-littlefs");
    // TEST_ASSERT_EQUAL_INT(0, res);
}

static void test_littlefs_teardown(void)
{
    flash_fs.unlink("/test-littlefs/test.txt");
    flash_fs.unlink("/test-littlefs/test0.txt");
    flash_fs.unlink("/test-littlefs/test1.txt");
    flash_fs.unlink("/test-littlefs/a/test2.txt");
    flash_fs.rmdir("/test-littlefs/a");
    flash_fs.rmdir("/test-littlefs");
    flash_fs.unmount();
}

static void tests_littlefs_format(void)
{
    int res;
    flash_fs.unmount();
    // res = mtd_erase(_dev, 0, flash_fs.cfg.block_size * _dev->sector_count);
    // TEST_ASSERT_EQUAL_INT(0, res);

    res = flash_fs.mount();
    TEST_ASSERT(res == 0);

    /* 1. format an invalid file system (failed mount) */
    res = flash_fs.format();
    TEST_ASSERT_EQUAL_INT(0, res);

    res = flash_fs.mount();
    TEST_ASSERT_EQUAL_INT(0, res);

    res = flash_fs.unmount();
    TEST_ASSERT_EQUAL_INT(0, res);

    /* 2. format a valid file system */
    res = flash_fs.format();
    TEST_ASSERT_EQUAL_INT(0, res);

    res = flash_fs.mount();
    TEST_ASSERT_EQUAL_INT(0, res);
}

static void tests_littlefs_mount_umount(void)
{
    int res;
    res = flash_fs.unmount();
    TEST_ASSERT_EQUAL_INT(0, res);

    res = flash_fs.mount();
    TEST_ASSERT_EQUAL_INT(0, res);
}

static void tests_littlefs_open_close(void)
{
    int res;
    FlashFile fd;
    res = fd.open_write("/test-littlefs/test.txt");
    TEST_ASSERT(res == 0);

    res = fd.close();
    TEST_ASSERT_EQUAL_INT(0, res);
}

static void tests_littlefs_write(void)
{
    const char buf[] = "TESTSTRING";
    char r_buf[2 * sizeof(buf)];

    int res;
    FlashFile fd;
    res = fd.open_write("/test-littlefs/test.txt");
    TEST_ASSERT(res == 0);

    res = fd.write(buf, sizeof(buf));
    TEST_ASSERT_EQUAL_INT(sizeof(buf), res);

    res = fd.fseek(0, SEEK_SET);
    TEST_ASSERT_EQUAL_INT(0, res);

    // res = fd.read(r_buf, sizeof(r_buf));
    // TEST_ASSERT_EQUAL_INT(sizeof(buf), res);
    // TEST_ASSERT_EQUAL_STRING(&buf[0], &r_buf[0]);

    res = fd.close();
    TEST_ASSERT_EQUAL_INT(0, res);

    res = fd.open_rdonly("/test-littlefs/test.txt");
    TEST_ASSERT(res >= 0);

    res = fd.read(r_buf, sizeof(r_buf));
    TEST_ASSERT_EQUAL_INT(sizeof(buf), res);
    TEST_ASSERT_EQUAL_STRING(&buf[0], &r_buf[0]);

    res = fd.close();
    TEST_ASSERT_EQUAL_INT(0, res);
}

static void tests_littlefs_unlink(void)
{
    const char buf[] = "TESTSTRING";

    int res;
    FlashFile fd;
    res = fd.open_write("/test-littlefs/test.txt");
    TEST_ASSERT(res >= 0);

    res = fd.write(buf, sizeof(buf));
    TEST_ASSERT_EQUAL_INT(sizeof(buf), res);

    res = fd.close();
    TEST_ASSERT_EQUAL_INT(0, res);

    res = flash_fs.unlink("/test-littlefs/test.txt");
    TEST_ASSERT_EQUAL_INT(0, res);
}

static void tests_littlefs_readdir(void)
{
    const char buf0[] = "TESTSTRING";
    const char buf1[] = "TESTTESTSTRING";
    const char buf2[] = "TESTSTRINGSTRING";
    FlashFile fd, fd0, fd1, fd2;

    int res;
    res = fd0.open_write("/test-littlefs/test0.txt");
    TEST_ASSERT(res >= 0);

    res = fd1.open_write("/test-littlefs/test1.txt");
    TEST_ASSERT(res >= 0);

    res = fd2.open_write("/test-littlefs/a/test2.txt");
    TEST_ASSERT(res < 0);

    res = flash_fs.mkdir("/test-littlefs/a");
    TEST_ASSERT_EQUAL_INT(0, res);

    res = fd.open_write("/test-littlefs/a/test2.txt");
    TEST_ASSERT(res >= 0);
    res = fd0.write(buf0, sizeof(buf0));
    TEST_ASSERT_EQUAL_INT(sizeof(buf0), res);

    res = fd1.write(buf1, sizeof(buf1));
    TEST_ASSERT_EQUAL_INT(sizeof(buf1), res);

    res = fd.write(buf2, sizeof(buf2));
    TEST_ASSERT_EQUAL_INT(sizeof(buf2), res);

    res = fd.close();
    TEST_ASSERT_EQUAL_INT(0, res);

    res = fd0.close();
    TEST_ASSERT_EQUAL_INT(0, res);

    res = fd1.close();
    TEST_ASSERT_EQUAL_INT(0, res);

    lfs_dir_t dirp;
    res = flash_fs.opendir(&dirp, "/test-littlefs");
    TEST_ASSERT_EQUAL_INT(0, res);

    lfs_info entry;
    int nb_files = 0;
    do
    {
        res = flash_fs.readdir(&dirp, &entry);
        if (res == 1 && (strcmp("test0.txt", entry.name) == 0 ||
                         strcmp("test1.txt",entry.name) == 0))
        {
            nb_files++;
        }
    } while (res == 1);

    TEST_ASSERT_EQUAL_INT(2, nb_files);
    TEST_ASSERT_EQUAL_INT(0, res);

    res = flash_fs.closedir(&dirp);
    TEST_ASSERT_EQUAL_INT(0, res);

    res = flash_fs.opendir(&dirp, "/test-littlefs/a");
    TEST_ASSERT_EQUAL_INT(0, res);
    nb_files = 0;
    do
    {
        res = flash_fs.readdir(&dirp, &entry);
        if (res == 1 && strcmp("test2.txt", &(entry.name[0])) == 0)
        {
            nb_files++;
        }
    } while (res == 1);

    TEST_ASSERT_EQUAL_INT(1, nb_files);
    TEST_ASSERT_EQUAL_INT(0, res);

    res = flash_fs.unlink("/test-littlefs/test0.txt");
    TEST_ASSERT_EQUAL_INT(0, res);

    res = flash_fs.unlink("/test-littlefs/test1.txt");
    TEST_ASSERT_EQUAL_INT(0, res);

    res = flash_fs.unlink("/test-littlefs/a/test2.txt");
    TEST_ASSERT_EQUAL_INT(0, res);

    res = flash_fs.closedir(&dirp);
    TEST_ASSERT_EQUAL_INT(0, res);

    res = flash_fs.rmdir("/test-littlefs/a");
    TEST_ASSERT_EQUAL_INT(0, res);
}

static void tests_littlefs_rename(void)
{
    const char buf[] = "TESTSTRING";
    char r_buf[2 * sizeof(buf)];

    int res;
    FlashFile fd;
    res = fd.open_write("/test-littlefs/test.txt");
    TEST_ASSERT(res >= 0);

    res = fd.write(buf, sizeof(buf));
    TEST_ASSERT(res == sizeof(buf));

    res = fd.fseek(0, SEEK_SET);
    TEST_ASSERT_EQUAL_INT(0, res);

    // res = fd.read(r_buf, sizeof(r_buf));
    // TEST_ASSERT(res == sizeof(buf));
    // TEST_ASSERT_EQUAL_STRING(&buf[0], &r_buf[0]);

    res = fd.close();
    TEST_ASSERT_EQUAL_INT(0, res);

    res = flash_fs.rename("/test-littlefs/test.txt", "/test-littlefs/test1.txt");
    TEST_ASSERT_EQUAL_INT(0, res);

    res = fd.open_rdonly("/test-littlefs/test.txt");
    TEST_ASSERT(res < 0);

    res = fd.open_rdonly("/test-littlefs/test1.txt");
    TEST_ASSERT(res >= 0);

    res = fd.fseek(0, SEEK_SET);
    TEST_ASSERT_EQUAL_INT(0, res);

    res = fd.read(r_buf, sizeof(r_buf));
    TEST_ASSERT(res == sizeof(buf));
    TEST_ASSERT_EQUAL_STRING(&buf[0], &r_buf[0]);

    res = fd.close();
    TEST_ASSERT_EQUAL_INT(0, res);

    res = flash_fs.unlink("/test-littlefs/test1.txt");
    TEST_ASSERT_EQUAL_INT(0, res);
}

static void tests_littlefs_statvfs(void)
{
    const char buf[] = "TESTSTRING";
    lfs_info stat1;
    lfs_info stat2;

    int res;
    flash_fs.stat("/test-littlefs/", &stat1);
    TEST_ASSERT_EQUAL_INT(0, res);
    // TEST_ASSERT_EQUAL_INT(flash_fs.cfg.block_size, stat1.f_bsize);
    // TEST_ASSERT_EQUAL_INT(flash_fs.cfg.block_size, stat1.f_frsize);
    // TEST_ASSERT((flash_fs.cfg.block_size*flash_fs.cfg.block_count) >=
    //                       stat1.f_blocks);

    FlashFile fd;
    res = fd.open_write("/test-littlefs/test.txt");
    TEST_ASSERT(res == 0);

    res = fd.write(buf, sizeof(buf));
    TEST_ASSERT(res == sizeof(buf));

    res = fd.close();
    TEST_ASSERT_EQUAL_INT(0, res);

    flash_fs.stat("/test-littlefs/", &stat2);
    TEST_ASSERT_EQUAL_INT(0, res);

    // TEST_ASSERT_EQUAL_INT(flash_fs.cfg.block_size, stat2.f_bsize);
    // TEST_ASSERT_EQUAL_INT(flash_fs.cfg.block_size, stat2.f_frsize);
    // TEST_ASSERT(stat1.f_bfree > stat2.f_bfree);
    // TEST_ASSERT(stat1.f_bavail > stat2.f_bavail);
}

Test *tests_littlefs(void)
{
#ifndef MTD_0
    // memset(dummy_memory, 0xff, sizeof(dummy_memory));
#endif

    EMB_UNIT_TESTFIXTURES(fixtures){
        new_TestFixture(tests_littlefs_format),
        new_TestFixture(tests_littlefs_mount_umount),
        new_TestFixture(tests_littlefs_open_close),
        new_TestFixture(tests_littlefs_write),
        new_TestFixture(tests_littlefs_unlink),
        new_TestFixture(tests_littlefs_readdir),
        new_TestFixture(tests_littlefs_rename),
        new_TestFixture(tests_littlefs_statvfs),
    };

    EMB_UNIT_TESTCALLER(littlefs_tests, test_littlefs_setup, test_littlefs_teardown, fixtures);

    return (Test *)&littlefs_tests;
}

int test_littlefs(void)
{
    TESTS_START();
    TESTS_RUN(tests_littlefs());
    TESTS_END();
    return 0;
}
/** @} */
